package zxj.weixin.mp.utils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 微信工具类
 * 
 * @author zhuxuejiang
 *
 */
public class WeixinUtils {

	private static final Logger LOGGER = LoggerFactory.getLogger(WeixinUtils.class);

	/**
	 * 验证消息是否来自微信服务器
	 * 
	 * 开发者通过检验signature对请求进行校验。 若确认此次GET请求来自微信服务器，请原样返回echostr参数内容，则接入生效，成为开发者成功，否则接入失败。
	 * 
	 * @param token     微信公众号令牌
	 * @param signature 微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
	 * @param timestamp 时间戳
	 * @param nonce     随机数
	 * @return 校验结果(true-验证通过；false-验证失败)
	 */
	public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {

		// 加密/校验流程如下：*、
		// 1. 将token、timestamp、nonce三个参数进行字典序排序
		String[] strArray = new String[] { token, timestamp, nonce };
		Arrays.sort(strArray);

		// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
		StringBuilder buffer = new StringBuilder();
		for (String str : strArray) {
			buffer.append(str);
		}
		String str = DigestUtils.sha1Hex(buffer.toString());

		// 3. 开发者获得加密后的字符串可与signature对比，标识该请求来源于微信
		return str == null ? false : str.equalsIgnoreCase(signature);
	}

	/**
	 * 获取JS-SDK中wx.config所需的配置信息
	 * 
	 * @param jsapiTicket 微信JS接口的临时票据
	 * @param url         URL
	 * @return 配置信息Map
	 * @throws UnsupportedEncodingException
	 */
	public static Map<String, String> getJsSdkConfig(String jsapiTicket, String url)
			throws UnsupportedEncodingException {
		try {
			url = URLDecoder.decode(url, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			LOGGER.error("获取JS-SDK中wx.config所需的配置信息失败：，url decode error", e);
			throw e;
		}

		String nonceStr = UUID.randomUUID().toString();
		String timestamp = Long.toString(System.currentTimeMillis() / 1000);

		String signature = DigestUtils.sha1Hex(
				"jsapi_ticket=" + jsapiTicket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url);

		Map<String, String> result = new HashMap<>(16);
		result.put("jsapi_ticket", jsapiTicket);
		result.put("nonceStr", nonceStr);
		result.put("timestamp", timestamp);
		result.put("url", url);
		result.put("signature", signature);

		return result;
	}

	/**
	 * 获取卡券扩展字段cardExt
	 * 
	 * @param apiTicket 调用卡券相关接口的临时票据
	 * @param cardId    卡券ID
	 * @param openid    用户的唯一标识
	 * @param outerStr  领取渠道参数，用于标识本次领取的渠道值
	 * @param code      指定的卡券code码，只能被领一次。自定义code模式的卡券必须填写，非自定义code和预存code模式的卡券不必填写
	 * @return 结果Map
	 */
	public static Map<String, String> getCardExt(String apiTicket, String cardId, String openid, String outerStr,
			String code) {
		String nonceStr = UUID.randomUUID().toString();
		String timestamp = Long.toString(System.currentTimeMillis() / 1000);

		// 1.将 api_ticket、timestamp、card_id、code、openid、nonce_str的value值进行字符串的字典序排序。
		List<String> list = new ArrayList<>();
		list.add(apiTicket);
		list.add(timestamp);
		list.add(cardId);
		list.add(openid);
		list.add(nonceStr);
		if (StringUtils.isNotBlank(code)) {
			list.add(code);
		}

		String[] strArray = new String[] {};
		strArray = list.toArray(strArray);
		Arrays.sort(strArray);
		StringBuilder buffer = new StringBuilder();
		for (String str : strArray) {
			buffer.append(str);
		}

		// 2.将所有参数字符串拼接成一个字符串进行sha1加密，得到signature。
		String signature = DigestUtils.sha1Hex(buffer.toString());

		Map<String, String> result = new HashMap<>(16);
		if (StringUtils.isNotBlank(code)) {
			result.put("code", code);
		}
		result.put("openid", openid);
		result.put("timestamp", timestamp);
		result.put("nonce_str", nonceStr);
		result.put("outer_str", outerStr);
		result.put("signature", signature);

		return result;
	}
}
